#include "qe_def.h"
#include "qe_log.h"
#include "qe_service.h"
#include "qe_memory.h"
#include "qe_buffer.h"
#include "qe_assert.h"



QELOG_DOMAIN(QELOG_DOMAIN_BUFFER);



qe_gbuf *qe_gbuf_new(qe_size size)
{
	int allocsize;
	qe_gbuf *buf;

	allocsize = sizeof(qe_gbuf) + size;
	buf = qe_malloc(allocsize);
	if (!buf) {return QE_NULL;}

	buf->p       = (char *)buf + sizeof(qe_gbuf);
	buf->size    = size;
	buf->nbytes  = 0;
	buf->dynamic = 1;

	return buf;
}

void qe_gbuf_init(qe_gbuf *buf, void *buffer, qe_size size)
{
	buf->p       = buffer;
	buf->size    = size;
	buf->nbytes  = 0;
	buf->dynamic = 0;
}

void qe_gbuf_clear(qe_gbuf *buf)
{
	qe_memset(buf->p, 0x0, buf->size);
	buf->nbytes = 0;
}

void qe_gbuf_backoff(qe_gbuf *buf, int back)
{
	qe_memmove(buf->p, buf->p+back, buf->size-back);
	buf->nbytes -= back;
}

qe_ret qe_gbuf_append(qe_gbuf *buf, void *data, qe_size length)
{
	unsigned int free = buf->size - buf->nbytes;

	if (length > free)
		return qe_err_notenough;

	qe_memcpy(buf->p + buf->nbytes, data, length);
	buf->nbytes += length;
	return qe_ok;
}

qe_ret qe_gbuf_memcpy(qe_gbuf *buf, qe_ptr data, qe_size size)
{
	if (buf->size < size)
		return qe_err_notenough;
	qe_memcpy(buf->p, data, size);
	buf->nbytes = size;
	return qe_ok;
}

void qe_gbuf_pool_init(qe_gbuf_pool *pool)
{
	pool->nbytes   = 0;
	pool->size     = 0;
	pool->num_bufs = 0;
	qe_list_init(&pool->bufs);
}

qe_gbuf_pool *qe_gbuf_pool_new(void)
{
	qe_gbuf_pool *pool = qe_malloc(sizeof(qe_gbuf_pool));
	qe_assert(pool != QE_NULL);
	pool->nbytes   = 0;
	pool->size     = 0;
	pool->num_bufs = 0;
	qe_list_init(&pool->bufs);
}

void qe_gbuf_pool_clear(qe_gbuf_pool *pool)
{
	qe_gbuf *p, *t;

	if (!pool)
		return;

	qe_list_foreach_entry_safe(p, t, &pool->bufs, list) {
		qe_list_remove(&p->list);
		if (p->dynamic) {
			qe_free(p);
		}
	}

	pool->nbytes   = 0;
	pool->size     = 0;
	pool->num_bufs = 0;
}

void qe_gbuf_pool_destroy(qe_gbuf_pool *pool)
{
	if (!pool)
		return;

	qe_gbuf_pool_clear(pool);

	qe_free(pool);
}

qe_ret qe_gbuf_pool_append(qe_gbuf_pool *pool, qe_gbuf *buf)
{
	qe_gbuf *p;

	if (!pool || !buf)
		return qe_err_param;
	
	qe_list_foreach_entry(p, &pool->bufs, list) {
		if (p->p == buf->p) {
			return qe_err_exist;
		}
	}

	qe_list_append(&p->list, &pool->bufs);
	pool->num_bufs++;
	pool->size += buf->size;
	pool->nbytes  += buf->nbytes;

	return qe_ok;
}

qe_gbuf *qe_gbuf_pool_merge(qe_gbuf_pool *pool)
{
	unsigned int free;
	qe_gbuf *p, *n;
	struct qe_list_node *node = QE_NULL;

	if (!pool || (!pool->num_bufs) || (!pool->size)) {
		return QE_NULL;
	}

	n = qe_gbuf_new(pool->size);
	if (!n) {
		return QE_NULL;
	}

	qe_list_foreach(node, &(pool->bufs)) {
		p = qe_list_entry(node, qe_gbuf, list);
		free = n->size - n->nbytes;
		if ((p->nbytes > 0) && (p->nbytes <= free)) {
			qe_memcpy(n->p + n->nbytes, p->p, p->nbytes);
			n->nbytes += p->nbytes;
		}
	}

	return n;
}

/**
 * @subpage General Buffer Ring
 */

/**
 * @brief GenBufRing initialization
 * @param[in] ring: GenBufRing pointer
 * @param[in] size: one buffer size
 * @param[in] num: buffer number
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_init(qe_gbuf_ring *ring, qe_size size, qe_uint num)
{
	int i;
	qe_gbuf *bd;

	if (!ring || !size || !num) {
		qe_error("invalid params");
		return qe_err_param;
	}

	/* alloc buffer space */
	ring->buf = qe_gbuf_new(size * num);
	if (!ring->buf) {
		qe_error("alloc buffer space fail");
		return qe_err_mem;
	}

	/* alloc descriptors */
	ring->bds = qe_malloc(sizeof(qe_gbuf) * num);
	if (!ring->bds) {
		qe_error("alloc descriptors fail");
		qe_free(ring->buf);
		return qe_err_mem;
	}

	ring->head         = 0;
	ring->tail         = 0;
	ring->count        = 0;
	ring->num_bufs     = num;
	ring->lock.lock    = QE_NULL;
	ring->lock.acquire = QE_NULL;
	ring->lock.release = QE_NULL;

	/* initialize descriptors */
	for (i=0; i<num; i++) {
		bd = &ring->bds[i];
		qe_gbuf_init(bd, qe_gbuf_offs_pos(ring->buf, i * size), size);
		qe_debug("bd[%d] pos %p", i, qe_gbuf_pos(bd));
	}
	qe_debug("ring size:%d num:%d %p init success", 
		size, num, qe_gbuf_pos(ring->buf));

	return qe_ok;
}

/**
 * @brief Create a GenBufRing
 * @param[in] size: one buffer size
 * @param[in] num: buffer number
 * @return qe_gbuf_ring
 */
qe_gbuf_ring *qe_gbuf_ring_new(qe_size size, qe_uint num)
{
	qe_ret ret;
	qe_gbuf_ring *ring;

	if (!size || !num) {
		qe_error("invalid params");
		return QE_NULL;
	}

	ring = qe_new(qe_gbuf_ring);
	if (!ring) {
		qe_error("alloc ring fail");
		return QE_NULL;
	}

	ret = qe_gbuf_ring_init(ring, size, num);
	if (ret != qe_ok) {
		qe_free(ring);
		return QE_NULL;
	}

	return ring;
}

/**
 * @brief Push data into GenBufRing
 * @param[in] ring: GenBufRing
 * @param[in] data: data pointer
 * @param[in] size: data length
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_push(qe_gbuf_ring *ring, qe_ptr data, qe_size size)
{
	qe_gbuf *bd;

	if (!ring || !data || !size) {
		qe_error("invalid params");
		return qe_err_param;
	}

	bd = &ring->bds[ring->tail];

	if (size > qe_gbuf_size(bd)) {
		qe_error("size error data size:%d > buf size:%d", 
			size, qe_gbuf_size(bd));
		return qe_err_range;
	}

	qe_gbuf_memcpy(bd, data, size);

	ring->tail = (ring->tail + 1) % ring->num_bufs;
	ring->count = qe_min(ring->count+1, ring->num_bufs);

	return qe_ok;
}

/**
 * @brief Pop buffer descriptor from GenBufRing
 * @param[in] ring: GenBufRing
 * @param[in] desc: buffer descriptor
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_pop(qe_gbuf_ring *ring, qe_gbuf *desc)
{
	qe_gbuf *bd;

	if (!ring || !desc) {
		qe_error("invalid params");
		return qe_err_param;
	}

	bd = &ring->bds[ring->head];

	if (ring->count == 0 || qe_gbuf_data_size(bd) == 0) {
		qe_warning("no data in ring");
		return qe_err_common;
	}

	qe_gbuf_pos(desc)       = qe_gbuf_pos(bd);
	qe_gbuf_size(desc)      = qe_gbuf_size(bd);
	qe_gbuf_data_size(desc) = qe_gbuf_data_size(bd);

	ring->head = (ring->head + 1) % ring->num_bufs;
	ring->count--;

	return qe_ok;
}

/**
 * @brief Peak buffer descriptor from GenBufRing tail
 * @param[in] ring: GenBufRing
 * @param[in] desc: buffer descriptor
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_peek_tail(qe_gbuf_ring *ring, qe_gbuf *desc)
{
	qe_gbuf *bd;

	if (!ring || !desc) {
		qe_error("invalid params");
		return qe_err_param;
	}

	bd = &ring->bds[ring->tail];

	qe_gbuf_pos(desc)       = qe_gbuf_pos(bd);
	qe_gbuf_size(desc)      = qe_gbuf_size(bd);
	qe_gbuf_data_size(desc) = qe_gbuf_data_size(bd);

	return qe_ok;
}

/**
 * @brief Peak buffer descriptor from GenBufRing head
 * @param[in] ring: GenBufRing
 * @param[in] desc: buffer descriptor
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_peek_head(qe_gbuf_ring *ring, qe_gbuf *desc)
{
	qe_gbuf *bd;

	if (!ring || !desc) {
		qe_error("invalid params");
		return qe_err_param;
	}

	bd = &ring->bds[ring->head];

	qe_gbuf_pos(desc)       = qe_gbuf_pos(bd);
	qe_gbuf_size(desc)      = qe_gbuf_size(bd);
	qe_gbuf_data_size(desc) = qe_gbuf_data_size(bd);

	return qe_ok;
}

/**
 * @brief Peak buffer descriptor from GenBufRing with index
 * @param[in] ring: GenBufRing
 * @param[in] index: peek index
 * @param[in] desc: buffer descriptor
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_peek_index(qe_gbuf_ring *ring, qe_uint index, 
	qe_gbuf *desc)
{
	qe_gbuf *bd;
	qe_uint offs;

	if (!ring || !desc) {
		qe_error("invalid params");
		return qe_err_param;
	}

	if (index >= ring->num_bufs) {
		qe_error("invalid index %d", index);
		return qe_err_range;
	}

	offs = (ring->head + index) % ring->num_bufs;
	bd = &ring->bds[offs];

	qe_gbuf_pos(desc)       = qe_gbuf_pos(bd);
	qe_gbuf_size(desc)      = qe_gbuf_size(bd);
	qe_gbuf_data_size(desc) = qe_gbuf_data_size(bd);

	return qe_ok;
}

/**
 * @brief GenBufRing set lock
 * @param[in] ring: GenBufRing
 * @param[in] lock: lock reference
 * @param[in] acquire: lock acquire function
 * @param[in] release: lock release function
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_set_lock(qe_gbuf_ring *ring, qe_ptr lock,
	qe_lock_acquire acquire, qe_lock_release release)
{
	if (!ring || !lock || !acquire || !release) {
		qe_error("invalid params");
		return qe_err_param;
	}

	ring->lock.lock    = lock;
	ring->lock.acquire = acquire;
	ring->lock.release = release;
	qe_debug("ring set lock %p", lock);
	return qe_ok;
}

/**
 * @brief Push data into GenBufRing with lock
 * @param[in] ring: GenBufRing
 * @param[in] data: data pointer
 * @param[in] size: data length
 * @param[in] wait: wait timeout
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_push_locked(qe_gbuf_ring *ring, qe_ptr data, 
	qe_size size, qe_uint wait)
{
	qe_ret ret;
	qe_gbuf *bd;
	qe_lock *lock;

	if (!ring || !data || !size) {
		qe_error("invalid params");
		return qe_err_param;
	}

	lock = &ring->lock;
	if (!lock->lock || !lock->acquire || !lock->release) {
		qe_error("no lock");
		return qe_err_param;
	}

	bd = &ring->bds[ring->tail];

	if (size > qe_gbuf_size(bd)) {
		qe_error("size error data size:%d > buf size:%d", 
			size, qe_gbuf_size(bd));
		return qe_err_range;
	}

	qe_gbuf_memcpy(bd, data, size);

	ret = lock->acquire(lock->lock, wait);
	if (ret != qe_ok)
		return ret;
	ring->tail = (ring->tail + 1) % ring->num_bufs;
	ring->count = qe_min(ring->count+1, ring->num_bufs);
	lock->release(lock->lock);

	return qe_ok;
}

/**
 * @brief Pop buffer descriptor from GenBufRing with lock
 * @param[in] ring: GenBufRing
 * @param[in] desc: buffer descriptor
 * @return qe_ret
 */
qe_ret qe_gbuf_ring_pop_locked(qe_gbuf_ring *ring, qe_gbuf *desc, 
	qe_uint wait)
{
	qe_ret ret;
	qe_gbuf *bd;
	qe_lock *lock;

	if (!ring || !desc) {
		qe_error("invalid params");
		return qe_err_param;
	}

	lock = &ring->lock;
	if (!lock->lock || !lock->acquire || !lock->release) {
		qe_error("no lock");
		return qe_err_param;
	}

	bd = &ring->bds[ring->head];

	if (ring->count == 0 || qe_gbuf_data_size(bd) == 0) {
		qe_warning("no data in ring");
		return qe_err_common;
	}

	qe_gbuf_pos(desc)       = qe_gbuf_pos(bd);
	qe_gbuf_size(desc)      = qe_gbuf_size(bd);
	qe_gbuf_data_size(desc) = qe_gbuf_data_size(bd);

	ret = lock->acquire(lock->lock, wait);
	if (ret != qe_ok)
		return ret;
	ring->head = (ring->head + 1) % ring->num_bufs;
	ring->count--;
	lock->release(lock->lock);

	return qe_ok;
}

/**
 * @brief Indicate if GenBufRing is full
 * @param[in] ring: GenBufRing
 * @return qe_bool
 */
qe_bool qe_gbuf_ring_isfull(qe_gbuf_ring *ring)
{
	return (ring->count == ring->num_bufs);
}

/**
 * @brief Indicate if GenBufRing is empty
 * @param[in] ring: GenBufRing
 * @return qe_bool
 */
qe_bool qe_gbuf_ring_isempty(qe_gbuf_ring *ring)
{
	return (ring->count == 0);
}

/**
 * @brief GenBufRing clear all descriptors
 * @param[in] ring: GenBufRing
 */
void qe_gbuf_ring_clear(qe_gbuf_ring *ring)
{
	int i;
	qe_gbuf *bd;

	if (!ring)
		return;

	for (i=0; i<ring->num_bufs; i++) {
		bd = &ring->bds[i];
		qe_gbuf_clear(bd);
	}
}

/**
 * @brief GenBufRing deinitialization
 * @param[in] ring: GenBufRing
 * @note This function use to free GenBufRing resource that GenBufRing is 
 * init by qe_gbuf_ring_init()
 */
void qe_gbuf_ring_deinit(qe_gbuf_ring *ring)
{
	if (!ring)
		return;
	if (ring->buf)
		qe_free(ring->buf);
	if (ring->bds)
		qe_free(ring->bds);
}

/**
 * @brief GenBufRing destroy
 * @param[in] ring: GenBufRing
 * @note This function use to free GenBufRing resource that GenBufRing is 
 * create by qe_gbuf_ring_new()
 */
void qe_gbuf_ring_destroy(qe_gbuf_ring *ring)
{
	if (!ring)
		return;
	qe_gbuf_ring_deinit(ring);
	qe_free(ring);
}

/**
 * @brief Initialize a RingBuffer with external buffer and destroy function
 * 
 * @param[in] rb: RingBuffer
 * @param[in] buf: external buffer pointer
 * @param[in] size: external buffer size
 * @param[in] destroy_func: external buffer destroy function
 * 
 * @note This function is the fully version of initializing the RingBuffer,
 * qe_ringbuffer_init() and qe_ringbuffer_new() will auto call this function.
 * RingBuffer support 3 create case as follow:
 *     1. call qe_ringbuffer_new() to dynamic create with a size. 
 *     2. call qe_ringbuffer_init() to init with a external static buffer.
 *     3. call qe_ringbuffer_init_full() to init with a external static/dynamic
 *        buffer and destroy function.
 * If in 'case 2', call qe_ringbuffer_destroy() will only clear data, but not
 * free buffer, if buffer is dynamic, you must manually release it. If in 
 * 'case 3', and qe_ringbuffer_init_full() give a destroy function, call 
 * qe_ringbuffer_destroy() will auto invoke destroy function. If in 'case 3' but
 * qe_ringbuffer_init_full() don't give destroy function, call 
 * qe_ringbuffer_destroy() only clear data.
 */
qe_ret qe_ringbuffer_init_full(qe_ringbuffer *rb, qe_ptr buf, 
	qe_size size, qe_destroy_notify destroy_func)
{
	qe_assert(rb != QE_NULL);

	if ((buf != QE_NULL && !size) ||
		(!buf && size>0)) {
		return qe_err_param;
	}

	rb->head         = 0;
	rb->tail         = 0;
	rb->count        = 0;
	rb->buf          = buf;
	rb->size         = size;
	rb->destroy_func = destroy_func;

	return qe_ok;
}

/**
 * @brief Initialize RingBuffer with external static buffer
 * 
 * @param[in] rb : RingBuffer
 * @param[in] buf : external buffer pointer
 * @param size : external buffer size
 */
qe_ret qe_ringbuffer_init(qe_ringbuffer *rb, qe_ptr buf, qe_size size)
{
	return qe_ringbuffer_init_full(rb, buf, size, QE_NULL);
}

/**
 * @brief RingBuffer default destroy notify
 */
void qe_ringbuffer_destroy_notify(qe_ptr data)
{
	qe_assert(data != QE_NULL);
	qe_free(data);
	data = QE_NULL;
}

/**
 * @brief Create a RingBuffer with size
 * 
 * @param size : the memsize to create
 * @return RingBuffer
 * @note use free to release RingBuffer's memory
 */
qe_ringbuffer *qe_ringbuffer_new(qe_size size)
{
	char *buf;
    
	qe_ringbuffer *rb = qe_malloc(sizeof(qe_ringbuffer)+size);
    if (!rb)
        return QE_NULL;
	
	buf = (char *)rb + sizeof(qe_ringbuffer);

	qe_ringbuffer_init_full(rb, buf, size, qe_ringbuffer_destroy_notify);

   	return rb;
}

/**
 * @brief Clear a RingBuffer, this function will not modify the data
 * 
 * @param rb : RingBuffer pointer
 */
void qe_ringbuffer_clear(qe_ringbuffer *rb)
{
	rb->head  = 0;
	rb->tail  = 0;
	rb->count = 0;
}

/**
 * @brief Write data to RingBuffer
 * 
 * @param[in] rb  : RingBuffer pointer
 * @param[in] buf : write buffer data
 * @param[in] len : write buffer length
 * 
 * @return RingBuffer wait length
 */
qe_uint qe_ringbuffer_write(qe_ringbuffer *rb, qe_ptr buf, qe_uint len)
{	
	qe_u8 *write_pos;
	qe_uint write_len;

	qe_assert(rb != QE_NULL);
	qe_assert(rb->buf != QE_NULL);
    qe_assert(buf != QE_NULL);

	write_pos = (qe_u8 *)buf;
	write_len = len;

	while (write_len--) {
		rb->buf[rb->tail] = *write_pos++;
		rb->tail = (rb->tail + 1) % rb->size;
		rb->count++;
		if ((rb->tail > rb->head) && ((rb->tail - rb->head) < rb->count)) {
			rb->count--;
			rb->head = (rb->head + 1) % rb->size;
		}
	}

	return rb->count;
}

/**
 * @brief Read data from RingBuffer
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 * @return read size
 */
qe_uint qe_ringbuffer_read(qe_ringbuffer *rb, char *buf, qe_uint len)
{
	qe_uint can_read;
	qe_uint read_bytes;
	qe_assert(rb != QE_NULL);
	qe_assert(rb->buf != QE_NULL);
	qe_assert(buf != QE_NULL);

	can_read = qe_min(len, rb->count);
	read_bytes = can_read;

	while (can_read--) {
		*buf++ = rb->buf[rb->head];
		rb->head = (rb->head + 1) % rb->size;
	}
	rb->count -= read_bytes;
	return read_bytes;
}

/**
 * @brief Peek data from RingBuffer, will not move head pos
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 * @return read size
 */
qe_uint qe_ringbuffer_peek(qe_ringbuffer *rb, char *buf, qe_uint len)
{
	qe_assert(rb != QE_NULL);
	qe_assert(rb->buf != QE_NULL);
	qe_assert(buf != QE_NULL);

	qe_uint head = rb->head;
	qe_uint read_size = qe_min(len, rb->count);

	while (read_size--) {
		*buf++ = rb->buf[head];
		head = (head + 1) % rb->size;
	}
	return read_size;
}

/**
 * @brief Remove data from RingBuffer
 * 
 * @param rb  : RingBuffer pointer
 * @param buf : read buffer pointer
 * @param len : read buffer length
 * 
 */
void qe_ringbuffer_remove(qe_ringbuffer *rb, qe_uint len)
{
	qe_assert(rb != QE_NULL);
	qe_assert(rb->buf != QE_NULL);
	
	qe_uint remove_size = qe_min(len, rb->count);
	rb->head = (rb->head + remove_size) % rb->size;
	rb->count -= remove_size;
}

/**
 * @brief Get the valid data size in RingBuffer
 * 
 * @param rb : RingBuffer pointer
 * @return wait data size
 */
qe_uint qe_ringbuffer_wait(qe_ringbuffer *rb)
{
	return rb->count;
}

/**
 * @brief Get RingBuffer free memory size
 * 
 * @param rb : RingBuffer pointer
 * @return free bytes
 */
qe_uint qe_ringbuffer_freesize(qe_ringbuffer *rb)
{
	if (rb->count >= rb->size)
		return 0;
	return rb->size - rb->count;
}

/**
 * @brief Get the RingBuffer's memory capacity
 * 
 * @param rb : RingBuffer pointer
 * @return capacity bytes
 */
qe_uint qe_ringbuffer_capacity(qe_ringbuffer *rb)
{
	return rb->size;
}

/**
 * @brief Indicate if RingBuffer is full
 * 
 * @return qe_true:full qe_false:not full
 */
qe_bool qe_ringbuffer_isfull(qe_ringbuffer *rb)
{
	return (rb->count == rb->size);
}

/**
 * @brief Indicate if RingBuffer is empty
 * 
 * @return qe_true:empty qe_false:not empty
 */
qe_bool qe_ringbuffer_isempty(qe_ringbuffer *rb)
{
	return (rb->count == 0);
}

/**
 * @brief Destory a RingBuffer
 * 
 * @param[in] rb: RingBuffer
 * 
 * @note This function will check RingBuffer's memory create way,
 * if use qe_ringbuffer_int() init RingBuffer with a external buffer,
 * this function will only 
 */
void qe_ringbuffer_destroy(qe_ringbuffer *rb)
{
	qe_assert(rb != QE_NULL);
	qe_assert(rb->buf != QE_NULL);

	if (rb->destroy_func) {
		rb->destroy_func(rb);
	} else {
		qe_ringbuffer_clear(rb);
	}
}

qe_ret qe_ringbuffer_iter_init(qe_ringbuffer_iter *iter, qe_ringbuffer *rb)
{
	if (!iter || !rb) {
		qe_error("invalid params");
		return qe_err_param;
	}

    iter->rb  = rb;
    iter->pos = -1;

	return qe_ok;
}

qe_bool qe_ringbuffer_iter_next(qe_ringbuffer_iter *iter, char *data)
{
	qe_uint index;

	if (!iter || !data) {
		qe_error("invalid params");
		return qe_err_param;
	}

	if (iter->pos >= iter->rb->count) {
		qe_error("iter pos:%d out of range:%u", iter->pos, 
			iter->rb->count);
		return qe_false;
	}

	iter->pos++;
	index = (iter->rb->head + iter->pos) % iter->rb->size;
	*data = iter->rb->buf[index];
	return qe_ok;
}